package md5Experiments;

import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;

public class Driver {

	public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
		Hash hash = new Hash();
		
		for (int i = 0; i < 64; i++) {
			System.out.println("Stage " + i + " starting.");
			Equation A[] = hash.getBits(96, 32);
			Equation B[] = hash.getBits(64, 32);
			Equation C[] = hash.getBits(32, 32);
			Equation D[] = hash.getBits(0, 32);
			Equation K[] = (new Hash(getK(i),0,0,0)).getBits(0, 32);
			Equation F[] = new Equation[32];
			int g = getG(i);
			Equation M[] = getMessage(g);
			System.out.println("Dwords acquired.");
			F = processDword(B,C,D,i/4);
			System.out.println("Process 1/6.");
			Equation temp[] = addDword(A,F);
			System.out.println("Process 2/6.");
			temp = addDword(temp,K);
			System.out.println("Process 3/6.");
			temp = addDword(temp,M);
			System.out.println("Process 4/6.");
			temp = rotateDword(temp, getS(i));
			System.out.println("Process 5/6.");
			temp = addDword(B,temp);
			System.out.println("Process 6/6.");
			hash.setBits(96,32,D);
			hash.setBits(64,32,temp);
			hash.setBits(32,32,B);
			hash.setBits(96,32,C);
			System.out.println("Stage " + i + " complete.");
		}
		
		hash.print();
	}
	
	public static Equation[] rotateDword(Equation[]A, int c){
		Equation eq[] = new Equation[32];
		for (int i = 0; i < 32; i++) {
			eq[i] = A[(i-c + 32)%32];
		}
		return eq;
	}
	
	public static Equation[] getMessage(int index){
		Equation eq[] = new Equation[32];
		for (int i = 0; i < 32; i++) {
			eq[i] = new Equation(index*32 + i);
		}
		return eq;
	}
	
	public static Equation[] addDword(Equation[] A, Equation[] B){
		Equation result[] = new Equation[32];
		Equation carry = new Equation(Equation.V_FALSE);
		for (int i = 0; i < 32; i++) {
			Equation temp = new Equation(Equation.V_XOR,A[i],B[i]);
			result[i] = new Equation(Equation.V_XOR,carry,temp);
			Equation temp2 = new Equation(Equation.V_AND,A[i],B[i]);
			carry = new Equation(Equation.V_AND,carry,temp);
			carry = new Equation(Equation.V_OR,temp2,carry);
		}
		return result;
	}
	
	public static long getBit(long i, int n){
		return (i >> n) & 1;
	}
	
	public static int getG(int i){
		if (i % 4 == 1)
			return i;
		else if (i % 4 == 2)
			return ((5*i) + 1)%16;
		else if (i % 4 == 2)			
			return ((3*i) + 5)%16;
		else{
			return ((7*i) + 1)%16;
		}
	}
	
	public static Equation[] processDword(Equation[] B,Equation[] C, Equation[] D,int type){
		Equation eq[] = new Equation[32];
		for (int i = 0; i < 32; i++) {
			eq[i] = processBit(B[i],C[i],D[i],type);
		}
		return eq;
	}
	
	public static Equation processBit(Equation B,Equation C, Equation D,int type){
		if (type == 1){
			Equation temp = new Equation(Equation.V_XOR,C,D);
			temp = new Equation(Equation.V_AND,B,temp);
			temp = new Equation(Equation.V_XOR,D,temp);
			return temp;
		}
		else if (type == 2){
			Equation temp = new Equation(Equation.V_XOR,B,C);
			temp = new Equation(Equation.V_AND,D,temp);
			temp = new Equation(Equation.V_XOR,C,temp);
			return temp;			
		}
		else if (type == 3){
			Equation temp = new Equation(Equation.V_XOR,C,D);
			temp = new Equation(Equation.V_XOR,C,temp);
			return temp;			
		}
		else{
			Equation temp = new Equation(Equation.V_NOT,D);
			temp = new Equation(Equation.V_OR,B,temp);
			temp = new Equation(Equation.V_XOR,C,temp);
			return temp;		
		}
	}
	
	public static long getK(int i){
		double val = Math.sin(i + 1);
		long valInt = (long)(val * (Math.pow(2, 32)));
		return (long) Math.floor(Math.abs(valInt));
	}
	
	public static int getS(int i){
		if (i < 16)
			return 7 + ((i % 4) * 5);
		else if (i < 32)
			if (i % 4 == 0)
				return 5;
			else if (i % 4 == 0)
				return 9;
			else if (i % 4 == 0)
				return 14;
			else
				return 20;
		else if (i < 48)
			if (i % 4 == 0)
				return 4;
			else if (i % 4 == 0)
				return 11;
			else if (i % 4 == 0)
				return 16;
			else
				return 23;
		else
			if (i % 4 == 0)
				return 6;
			else if (i % 4 == 0)
				return 10;
			else if (i % 4 == 0)
				return 15;
			else
				return 21;
	}

}
